Desbloqueie animações suaves e performáticas baseadas em scroll com CSS puro. Este guia aborda animation-timeline e animation-range para um controlo preciso.
CSS Animation Range: Um Mergulho Profundo no Controlo de Animações Guiadas por Scroll
Durante anos, criar animações que reagem à posição de scroll do utilizador tem sido um pilar de experiências web envolventes. De subtis fade-ins a complexos efeitos de paralaxe, estas interações dão vida a páginas estáticas. No entanto, tradicionalmente, vieram com um custo significativo: a dependência de JavaScript. Bibliotecas e scripts personalizados que escutam eventos de scroll podem ser intensivos em termos de desempenho, executando na thread principal e potencialmente levando a experiências de utilizador instáveis e sem resposta, especialmente em dispositivos menos potentes.
Entre numa nova era da animação web. Os mais recentes avanços em CSS estão a revolucionar a forma como lidamos com estas interações. A especificação Animações Guiadas por Scroll (Scroll-Driven Animations) fornece uma maneira poderosa, declarativa e altamente performática de vincular animações diretamente à posição de uma barra de scroll ou à visibilidade de um elemento dentro da viewport—tudo sem uma única linha de JavaScript.
No centro deste novo paradigma estão duas propriedades-chave: animation-timeline e animation-range. Enquanto animation-timeline prepara o terreno, definindo o que impulsiona a animação (por exemplo, a barra de scroll do documento), é animation-range que nos dá o controlo granular que sempre desejámos. Permite-nos definir os pontos de início e fim precisos de uma animação dentro dessa linha do tempo.
Neste guia abrangente, vamos explorar o mundo das Animações Guiadas por Scroll em CSS com um foco especial em animation-range. Abordaremos:
- Os conceitos fundamentais das Linhas do Tempo de Progresso de Scroll e de Visualização.
- Uma análise detalhada da propriedade
animation-rangee da sua sintaxe. - Exemplos práticos e do mundo real para criar barras de progresso, efeitos de revelação e muito mais.
- As melhores práticas para desempenho, acessibilidade e compatibilidade entre navegadores.
Prepare-se para desbloquear animações que não são apenas bonitas, mas também incrivelmente eficientes, movendo a lógica complexa da thread principal para a thread do compositor para uma viagem garantidamente suave como seda.
Compreender os Fundamentos: O que são Animações Guiadas por Scroll?
Antes de mergulharmos em animation-range, é crucial entender o sistema em que opera. Tradicionalmente, as animações CSS estão ligadas a uma linha do tempo baseada em tempo. Quando se define animation-duration: 3s;, a animação progride de 0% a 100% ao longo de três segundos, impulsionada por um relógio.
As Animações Guiadas por Scroll mudam isto fundamentalmente. Elas introduzem o conceito de uma Linha do Tempo de Progresso, que não é impulsionada pelo tempo, mas pelo progresso—seja o progresso de rolar um contentor ou o progresso da visibilidade de um elemento à medida que se move pela viewport.
Este novo modelo oferece três grandes vantagens:
- Desempenho: Como estas animações podem ser executadas fora da thread principal, na thread do compositor do navegador, elas não competem por recursos com JavaScript, layout ou operações de pintura. O resultado é uma animação excecionalmente suave, livre da instabilidade que muitas vezes afeta os ouvintes de scroll baseados em JS.
- Simplicidade: A sintaxe CSS é declarativa. Você declara o que quer que aconteça, e o navegador trata dos cálculos complexos. Isto muitas vezes leva a um código mais limpo e de fácil manutenção em comparação com o JavaScript imperativo.
- Acessibilidade: As animações respeitam as preferências do utilizador, como
prefers-reduced-motion, desde o início, facilitando a construção de experiências inclusivas.
Existem dois tipos principais de linhas do tempo de progresso com as quais irá trabalhar:
- Linha do Tempo de Progresso de Scroll: Acompanha a posição de scroll dentro de um contentor de scroll (um "scroller"). A linha do tempo representa todo o intervalo rolável, desde o topo (0%) até ao fundo (100%).
- Linha do Tempo de Progresso de Visualização: Acompanha a visibilidade de um elemento à medida que ele cruza a viewport. A linha do tempo representa a jornada do elemento desde o momento em que entra na viewport até sair completamente.
O Conceito Central: A Propriedade `animation-timeline`
O primeiro passo para criar uma animação guiada por scroll é desassociar uma animação CSS padrão do seu relógio baseado em tempo e associá-la a uma nova linha do tempo baseada em progresso. Isto é feito usando a propriedade animation-timeline.
Ela aceita uma função que define a origem da linha do tempo: scroll() para uma Linha do Tempo de Progresso de Scroll ou view() para uma Linha do Tempo de Progresso de Visualização.
Linha do Tempo de Progresso de Scroll: `scroll()`
A função scroll() vincula uma animação à posição de scroll de um contentor. A sua forma mais comum é scroll(scroller, axis).
scroller: Especifica qual o elemento de scroll a acompanhar. Pode serroot(a viewport do documento),self(o próprio elemento, se for um scroller), ounearest(o ancestral scroller mais próximo).axis: Especifica o eixo de scroll a acompanhar. Pode serblock(a direção principal do fluxo de conteúdo, geralmente vertical),inline(perpendicular ao block, geralmente horizontal),y, oux.
Exemplo: Uma Barra de Progresso Simples de Scroll do Documento
Vamos criar uma barra de progresso no topo da página que cresce à medida que o utilizador rola para baixo.
<!-- HTML -->
<div id="progress-bar"></div>
<!-- CSS -->
@keyframes grow-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
#progress-bar {
position: fixed;
top: 0;
left: 0;
height: 10px;
width: 100%;
background-color: dodgerblue;
transform-origin: left;
/* Desassocia do tempo, associa ao scroll do documento */
animation: grow-progress linear;
animation-timeline: scroll(root block);
}
Neste exemplo, a animação grow-progress é agora impulsionada pelo scroll do documento principal (root) no seu eixo vertical (block). À medida que rola de 0% a 100% da página, a transformação scaleX da barra de progresso vai de 0 a 1.
Linha do Tempo de Progresso de Visualização: `view()`
A função view() vincula uma animação à visibilidade de um elemento dentro do seu scroller. Isto é incrivelmente útil para acionar animações de "revelação" à medida que os elementos entram no campo de visão.
A sua sintaxe é view(axis, inset).
axis: Opcional, os mesmos valores que emscroll()(ex:block). Define qual o eixo do scrollport a considerar.inset: Opcional, permite ajustar os limites da viewport usados para calcular a visibilidade, semelhante aorootMargindoIntersectionObserver.
Exemplo: Fazer um Elemento Aparecer Gradualmente (Fade In)
<!-- HTML -->
<div class="content-box reveal">
Esta caixa aparecerá gradualmente ao entrar no ecrã.
</div>
<!-- CSS -->
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
.reveal {
/* Associa a animação à visibilidade deste elemento */
animation: fade-in linear;
animation-timeline: view();
}
Aqui, a animação fade-in está ligada ao próprio elemento .reveal. A animação irá progredir à medida que o elemento viaja pela viewport. Mas como é que isto é mapeado exatamente? Quando começa e acaba? É aí que entra o animation-range.
A Estrela do Espetáculo: `animation-range`
Enquanto animation-timeline define o contexto, animation-range fornece o controlo crítico. Ele define que parte da linha do tempo é considerada "ativa" e mapeia-a para o progresso de 0% a 100% da sua animação @keyframes.
A sintaxe básica é:
animation-range: <range-start> <range-end>;
Isto diz ao navegador: "Quando a linha do tempo atingir o ponto <range-start>, a animação deve estar a 0%. Quando atingir o ponto <range-end>, a animação deve estar a 100%."
Os valores para <range-start> e <range-end> podem ser de vários tipos:
- Palavras-chave (para
view()): Nomes especiais e altamente intuitivos comoentry,exit,coverecontain. Vamos explorá-los em detalhe. - Percentagens: Uma percentagem da duração total da linha do tempo. Para uma linha do tempo
scroll(),0%é o topo e100%é o fundo. - Comprimentos CSS: Um valor de comprimento fixo como
100pxou20rem. Isto especifica um ponto nesse deslocamento de scroll desde o início da linha do tempo.
Pode até combinar palavras-chave com percentagens ou comprimentos para um controlo extremamente preciso, como entry 50% ou cover 200px.
Mergulho Prático: `animation-range` com Linhas do Tempo `scroll()`
Ao trabalhar com uma linha do tempo scroll(), está a mapear a sua animação para o intervalo de scroll geral do scroller. Vejamos como o animation-range nos ajuda a visar partes específicas dessa jornada.
Visar uma Secção de Scroll Específica
Imagine que tem um artigo longo e quer que um gráfico específico anime apenas enquanto o utilizador está a rolar pela metade central da página.
@keyframes spin-and-grow {
from { transform: rotate(0deg) scale(0.5); opacity: 0; }
to { transform: rotate(360deg) scale(1); opacity: 1; }
}
.special-graphic {
animation: spin-and-grow linear;
animation-timeline: scroll(root block);
/* A animação começa em 25% do scroll e termina em 75% */
animation-range: 25% 75%;
}
Como funciona:
- Antes de o utilizador ter rolado 25% da página, a animação é mantida no seu estado de 0% (
rotate(0deg) scale(0.5) opacity: 0). - À medida que o utilizador rola da marca de 25% para a de 75%, a animação progride de 0% a 100%.
- Depois de o utilizador passar da marca de 75%, a animação é mantida no seu estado de 100% (
rotate(360deg) scale(1) opacity: 1).
Esta simples adição de animation-range dá-nos um controlo poderoso sobre o tempo e a colocação dos nossos efeitos dentro da experiência de scroll mais ampla.
Usar Comprimentos Absolutos
Também pode usar comprimentos absolutos. Por exemplo, se quiser que uma animação ocorra apenas nos primeiros 500 pixels de scroll:
.hero-animation {
animation: fade-out linear;
animation-timeline: scroll(root block);
/* A animação começa no deslocamento de scroll 0px e termina em 500px */
animation-range: 0px 500px;
}
Isto é perfeito para animações introdutórias na secção principal de uma página que devem terminar assim que o utilizador começar a rolar mais para dentro do conteúdo.
Dominar o `animation-range` com Linhas do Tempo `view()`
É aqui que o animation-range se torna verdadeiramente mágico. Quando usado com uma linha do tempo view(), os valores do intervalo não se baseiam na altura total do scroll do documento, mas na visibilidade do elemento dentro da viewport. É aqui que os intervalos nomeados especiais entram em jogo.
Explicação dos Intervalos Nomeados
Imagine um elemento (o "sujeito") e a viewport (o "scroller"). Os intervalos nomeados descrevem a relação entre estas duas caixas.
entry: A fase em que o sujeito está a entrar na viewport. Começa no momento em que a borda inferior do sujeito cruza a borda superior da viewport e termina quando a borda inferior do sujeito cruza a borda inferior da viewport.exit: A fase em que o sujeito está a sair da viewport. Começa quando a borda superior do sujeito cruza a borda superior da viewport e termina quando a borda superior do sujeito cruza a borda inferior da viewport.cover: A fase em que o sujeito é grande o suficiente para cobrir completamente a viewport. Começa quando a borda superior do sujeito atinge a borda superior da viewport e termina quando a borda inferior do sujeito atinge a borda inferior da viewport. Se o sujeito for menor que a viewport, esta fase nunca ocorre.contain: A fase em que o sujeito está totalmente contido na viewport. Começa quando a borda inferior do sujeito entra na borda inferior da viewport e termina quando a borda superior do sujeito sai da borda superior da viewport. Se o sujeito for maior que a viewport, esta fase nunca ocorre.
Exemplo Prático: O Clássico Efeito "Revelar ao Rolar"
Vamos recriar uma das animações baseadas em scroll mais comuns: um elemento que aparece gradualmente e desliza para a vista à medida que entra no ecrã. Tradicionalmente, isto exigia um Intersection Observer em JavaScript. Agora, são algumas linhas de CSS.
<!-- HTML -->
<section>
<div class="content-box reveal">Caixa 1</div>
<div class="content-box reveal">Caixa 2</div>
<div class="content-box reveal">Caixa 3</div>
</section>
<!-- CSS -->
@keyframes fade-and-slide-in {
from { opacity: 0; transform: translateY(50px); }
to { opacity: 1; transform: translateY(0); }
}
.reveal {
animation: fade-and-slide-in linear both; /* 'both' é importante! */
animation-timeline: view();
/* Começa a animação quando o elemento entra, termina quando está a meio da entrada */
animation-range: entry 0% entry 50%;
}
Vamos analisar esse valor de animation-range:
animation-fill-mode: both;é crucial. Garante que, antes do intervalo ativo da animação, o elemento permaneça no seu estadofrom(invisível e deslocado para baixo) e, após o intervalo, permaneça no seu estadoto(totalmente visível e no lugar).entry 0%: O ponto de partida. Refere-se ao início da faseentry—o momento exato em que a parte inferior do nosso elemento toca na parte inferior da viewport.entry 50%: O ponto final. Refere-se ao momento em que o elemento completou 50% da sua jornada através da faseentry. Neste ponto, a animação estará 100% completa.
Isto proporciona um efeito agradável em que o item está totalmente visível e na sua posição final bem antes de o utilizador o ter rolado para o centro do ecrã. Pode ajustar estas percentagens para obter a sensação exata que deseja. Por exemplo, entry 25% entry 75% criaria uma animação mais prolongada.
Controlo Avançado: Criar um Efeito de Paralaxe
Vamos tentar um efeito mais complexo. Faremos uma imagem de fundo mover-se a uma velocidade diferente da do scroll, mas apenas enquanto o seu contentor estiver a cobrir a viewport.
<!-- HTML -->
<div class="parallax-container">
<div class="parallax-bg"></div>
<h2>Secção de Paralaxe</h2>
</div>
<!-- CSS -->
@keyframes parallax-shift {
from { background-position: 50% -50px; }
to { background-position: 50% 50px; }
}
.parallax-container {
position: relative;
height: 100vh;
overflow: hidden;
}
.parallax-bg {
position: absolute;
inset: -50px; /* Torna-o mais alto que o contentor para permitir movimento */
background-image: url('your-image.jpg');
background-size: cover;
animation: parallax-shift linear both;
animation-timeline: view(block);
/* Anima ao longo de toda a fase 'cover' */
animation-range: cover 0% cover 100%;
}
Neste caso, a animação parallax-shift está ligada à linha do tempo do elemento parallax-bg. O animation-range está definido para a duração total da fase cover. Isto significa que a animação só começa a progredir quando o contentor é alto o suficiente para cobrir a viewport e está posicionado de forma que o seu topo esteja no topo da viewport. Termina quando a parte inferior do contentor atinge a parte inferior da viewport. O resultado é um efeito de paralaxe suave e performático que está perfeitamente sincronizado com a posição do scroll.
Combinar Tudo: Atalhos e Melhores Práticas
O Atalho `animation`
Para tornar a sintaxe ainda mais concisa, as propriedades de linha do tempo e de intervalo podem ser incluídas diretamente na propriedade de atalho animation. Esta é uma nova sintaxe proposta que está a ganhar suporte.
O nosso exemplo de revelar ao rolar poderia ser reescrito como:
.reveal {
animation: fade-and-slide-in linear both view() entry 0% entry 50%;
}
Esta única linha substitui as três propriedades separadas animation, animation-timeline e animation-range. É limpo, eficiente e mantém toda a lógica da animação num só lugar.
Considerações de Desempenho
O principal benefício das animações guiadas por scroll é o desempenho. Para manter este benefício, deve dar prioridade à animação de propriedades que podem ser tratadas pela thread do compositor. Estas são principalmente:
transform(translate, scale, rotate)opacity
Animar propriedades como width, height, margin, ou color ainda funcionará, mas podem acionar operações de layout e pintura, que ocorrem na thread principal. Embora ainda sejam muitas vezes mais suaves do que as alternativas baseadas em JS, não serão tão performáticas como as animações exclusivas do compositor.
Acessibilidade e Fallbacks
É crucial construir para todos os utilizadores. As animações guiadas por scroll são ótimas, mas alguns utilizadores acham o movimento distrativo ou nauseante.
1. Respeite as Preferências do Utilizador: Envolva sempre o seu CSS relacionado com movimento numa media query prefers-reduced-motion.
@media (prefers-reduced-motion: no-preference) {
.reveal {
animation: fade-and-slide-in linear both;
animation-timeline: view();
animation-range: entry 0% entry 50%;
}
}
2. Forneça Fallbacks para Navegadores Antigos: Como esta é uma tecnologia nova, deve ter em conta os navegadores que ainda não a suportam. A regra @supports é a sua melhor amiga aqui. Forneça um estado padrão simples e não animado, e depois melhore-o para os navegadores que o suportam.
/* Estado padrão para todos os navegadores */
.reveal {
opacity: 1;
transform: translateY(0);
}
/* Melhoria para navegadores que suportam */
@supports (animation-timeline: view()) {
@media (prefers-reduced-motion: no-preference) {
.reveal {
opacity: 0; /* Define o estado inicial para a animação */
transform: translateY(50px);
animation: fade-and-slide-in linear both;
animation-timeline: view();
animation-range: entry 0% entry 50%;
}
}
}
Suporte de Navegadores e Olhando para o Futuro
No final de 2023, as Animações Guiadas por Scroll em CSS são suportadas no Chrome e no Edge. Estão em desenvolvimento ativo no Firefox e a ser consideradas pelo Safari. Como com qualquer funcionalidade de ponta da plataforma web, é essencial verificar recursos como CanIUse.com para obter as informações de suporte mais recentes.
A introdução desta tecnologia marca uma mudança significativa no desenvolvimento web. Capacita designers e developers a criar experiências ricas, interativas e performáticas de forma declarativa, reduzindo a nossa dependência de JavaScript para toda uma classe de padrões de UI comuns. À medida que o suporte dos navegadores amadurece, espere ver as animações guiadas por scroll tornarem-se uma ferramenta essencial no kit de ferramentas de todo o developer front-end.
Conclusão
As Animações Guiadas por Scroll em CSS, e especificamente a propriedade animation-range, representam um salto monumental para a animação web. Passámos de linhas do tempo baseadas no tempo para linhas do tempo baseadas no progresso, desbloqueando a capacidade de criar interações complexas e cientes do scroll com desempenho e simplicidade inigualáveis.
Aprendemos que:
animation-timelineliga uma animação a uma linha do tempo de progressoscroll()ouview().animation-rangedá-nos um controlo preciso, mapeando uma porção específica dessa linha do tempo para os keyframes da nossa animação.- Com linhas do tempo
view(), intervalos nomeados poderosos comoentry,exit,coverecontainfornecem uma maneira intuitiva de controlar animações com base na visibilidade de um elemento. - Ao nos atermos a propriedades amigáveis ao compositor e ao fornecer fallbacks, podemos usar esta tecnologia hoje para construir experiências de utilizador acessíveis, performáticas e encantadoras.
Os dias de lutar com ouvintes de scroll instáveis e que bloqueiam a thread principal para efeitos simples estão contados. O futuro da animação baseada em scroll está aqui, é declarativo e está escrito em CSS. É hora de começar a experimentar.